﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Lossage.Net.Protocols.Connection
{
    internal class TCPClient : IConnectionClient
    {
        System.Net.Sockets.TcpClient client;
        bool useAutoReconnect;
        string ip;
        int port;
        int timeOutMilsecond;
        public bool IsConnecting { get; private set; }

        public TCPClient(string ip, int port, int timeOutMilsecond, bool useAutoReconnect)
        {
            this.ip = ip;
            this.port = port;
            this.timeOutMilsecond = timeOutMilsecond;
            this.useAutoReconnect = useAutoReconnect;
            this.client = new System.Net.Sockets.TcpClient();

            if (useAutoReconnect)
            {
                this.autoReconnect(false);
            }
        }

        public void autoReconnect(bool async)
        {
            Action reconnectFunc = () =>
            {
                if (this.IsConnecting)
                {
                    return;
                }
                this.IsConnecting = true;
                for (int i = 0; !this.connectInternal(); i++)
                {
                    if (i < 3)
                    {
                        System.Threading.Thread.Sleep(3000);
                    }
                    else if (i < 5)
                    {
                        System.Threading.Thread.Sleep(15000);
                    }
                    else if (i < 10)
                    {
                        System.Threading.Thread.Sleep(30000);
                    }
                    else if (i < 50)
                    {
                        System.Threading.Thread.Sleep(60000);
                    }
                    else
                    {
                        System.Threading.Thread.Sleep(180000);
                    }
                }
                this.IsConnecting = false;
            };
            if (async)
            {
                this.IsConnecting = true;
                System.Threading.Tasks.Task.Run(reconnectFunc);
            }
            else
            {
                reconnectFunc();
            }
        }

        public bool Connect()
        {
            if (this.useAutoReconnect)
            {
                this.autoReconnect(false);
                return true;
            }
            else
            {
                return this.connectInternal();
            }
        }

        private bool connectInternal()
        {
            if (this.client != null && this.client.Connected)
            {
                return true;
            }
            else
            {
                this.client = new System.Net.Sockets.TcpClient();
            }
            try
            {
                this.client.Connect(this.ip, this.port);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
                return false;
            }
            return this.client.Connected;
        }

        public IEnumerable<byte> Read()
        {
            if (this.timeOutMilsecond > 0)
            {
                this.client.ReceiveTimeout = this.timeOutMilsecond;

            }
            var buffer = new byte[4096];
            try
            {
                var len = this.client.GetStream().Read(buffer, 0, buffer.Length);
                return buffer.Take(len);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
                this.autoReconnect(false);
                return null;
            }
        }

        public bool Write(IEnumerable<byte> buffer)
        {
            if (this.timeOutMilsecond > 0)
            {
                this.client.SendTimeout = this.timeOutMilsecond;
            }
            try
            {
                this.client.GetStream().Write(buffer.ToArray(), 0, buffer.Count());
                return true;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
                this.autoReconnect(false);
                return false;
            }
        }
    }
}
